/*
 JSON syntax parser, returns json tree
*/

#define JSON_PRIVATE_
#include "json.h"

static json_node*    root   = 0; // JSON-tree root
static unsigned char state  = 0; // Current parser state

// Context stack for nesting JSON. For example: {"A":1, {"B":2}}
static unsigned char context[JSON_PARSER_NEST_MAX];
static unsigned char tail = 0;

static inline void push(unsigned char s)
{
  if (tail < JSON_PARSER_NEST_MAX)
    context[tail++] = s;
  else
    json_error("Parser stack overrun");
}

static inline void pop(){ 
  if (tail > 0 && tail < JSON_PARSER_NEST_MAX)
    state = context[--tail];
  else 
    json_error("Parser stack underrun");
}

void json_parse_init()
{
  tail  = 0;
  state = 0;
  root  = NULL;
  json_lexer_reset();
}

bool json_parse_string(char* str, json_node** node)
{
  json_parse_init();
  while(str++) {
    if (json_parse_char(*str, node)) return true;
  }
  return false;
}

bool json_parse_char(char c, json_node** node)
{
  bool accepted = false;
  if (node == NULL) return false;
  if (*node == NULL) *node = json_node_create();
  if (root == NULL) root = *node;
  do {
    Symbol sym = {empty, NULL, 0};
    accepted = json_get_symbol(c, &sym); // Repeat lexer routines until character not accepted
    if (sym.sym != empty) {
      if (json_parse_symbol(&sym)) 
        return true;
    }
  }while (accepted == false); 
  return false;
}

bool json_parse_symbol(Symbol* symbol)
{
  switch(state){
    case 0:
      if (symbol->sym == lbrace) {
        state = 1;
      }
      else 
        json_error("{ or empty expected");
      break;
    case 1:
      if (symbol->sym == string) {
        state = 2;
        json_node_set_name(root, symbol->id, symbol->len);
      }
      else json_error("string (name) expected");
      break;
    case 2:
      if (symbol->sym == colon) state = 3;
      else json_error(": expected");
      break;
    case 3:
      if (symbol->sym == integer || symbol->sym == real || symbol->sym == string){
        state = 4;
        json_set_value(symbol);
      }
      else if (symbol->sym == lbrace) {
        json_node_add_child(root);
        root = root->child;
        push(4);
        state = 1;
      }
      else if (symbol->sym == lbrak){
        state = 6;
      }
      else json_error("invalid symbol received");
      break;
    case 4:
      if (symbol->sym == comma) {
        json_node_add_next(root);
        root = root->next;
        state = 1;
      }
      else if (symbol->sym == rbrace && root->parent) {
        root = root->parent;
        
        pop();
      }
      else if (symbol->sym == rbrace){
        state = 0;
        while(root->parent) root = root->parent; // Find tree root
        while(root->prev)   root = root->prev;
        return true;
      }
      else json_error("} or , expected");
      break;
    case 5:
      break;
    case 6:
      if (symbol->sym == rbrak){
        state = 4;
      }
      else if (symbol->sym == comma){}
      else if (symbol->sym == integer || symbol->sym == real || symbol->sym == string) 
      {
        json_add_value(symbol);
      }
      else json_error("invalid array value received");
      break;
  }
  return false;
}

void json_error(char message[])
{
  // TODO: Print JSON error here
}

// Set JSON node parameters with the Symbol data
void json_set_value(Symbol* symbol)
{
  if (symbol->sym == integer){
    int value = atoi(symbol->id);
    json_node_set_value(root, &value, symbol->sym, sizeof(value));
  }
  else if (symbol->sym == real){
    double value = atof(symbol->id);
    json_node_set_value(root, &value, symbol->sym, sizeof(value));
  }
  else if (symbol->sym == string){
    json_node_set_value(root, symbol->id, symbol->sym, symbol->len);
  }
}

// Add a new value to a JSON array
void json_add_value(Symbol* symbol)
{
  if (symbol->sym == integer){
    int value = atoi(symbol->id);
    json_node_add_value(root, &value, symbol->sym, sizeof(value));
  }
  else if (symbol->sym == real){
    double value = atof(symbol->id);
    json_node_add_value(root, &value, symbol->sym, sizeof(value));
  }
  else if (symbol->sym == string){
    json_node_add_value(root, &symbol->id, symbol->sym, symbol->len);
  }
}